﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.DirectoryServices;
using System.Linq;
using System.Text;

namespace Library
{
  /// <summary>
  /// 組織単位（OU）を表すクラスです。
  /// </summary>
  [DataObject()]
  public sealed class OrganizationalUnit : DomainObject
  {
    #region プライベートフィールド
    private List<DomainObject> _storedObjects;
    #endregion

    #region インターナルコンストラクター
    /// <summary>
    /// <see cref="DirectoryEntry"/> を指定して <see cref="OrganizationalUnit"/> クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="entry"><see cref="DirectoryEntry"/>。</param>
    internal OrganizationalUnit(DirectoryEntry entry)
      : base(entry)
    {
    }
    #endregion

    #region パブリックプロパティ
    /// <summary>
    /// 市区町村を取得または設定します。
    /// </summary>
    public string City
    {
      get
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        return (string)base.Entry.Properties["l"].Value;
      }
      set
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        base.Entry.Properties["l"].Value = value;
      }
    }

    /// <summary>
    /// 郵便番号を取得または設定します。
    /// </summary>
    public string PostalCode
    {
      get
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        return (string)base.Entry.Properties["postalCode"].Value;
      }
      set
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        base.Entry.Properties["postalCode"].Value = value;
      }
    }

    /// <summary>
    /// 都道府県を取得または設定します。
    /// </summary>
    public string Prefecture
    {
      get
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        return (string)base.Entry.Properties["st"].Value;
      }
      set
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        base.Entry.Properties["st"].Value = value;
      }
    }

    /// <summary>
    /// 直下のオブジェクトを取得します。
    /// </summary>
    public IList<DomainObject> StoredDomainObjects
    {
      get
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        if (_storedObjects == null)
        {
          _storedObjects = DirectoryAccess.GetStoredDomainObjects(this);  //直下のオブジェクトを取得
        }
        return _storedObjects;
      }
    }

    /// <summary>
    /// 番地を取得または設定します。
    /// </summary>
    public string Street
    {
      get
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        return (string)base.Entry.Properties["street"].Value;
      }
      set
      {
        if (base.IsDisposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        base.Entry.Properties["street"].Value = value;
      }
    }
    #endregion

    #region パブリックメソッド
    /// <summary>
    /// 指定したパスの組織単位（OU）を検索します。このメソッドはデータバインド用です。
    /// </summary>
    /// <param name="path">パス。</param>
    /// <returns>組織単位（OU）。</returns>
    [DataObjectMethod(DataObjectMethodType.Select)]
    public static OrganizationalUnit FindByPath(string path)
    {
      //distinguishedNameを生成
      var ouNames = path.Split('/').Reverse();
      var sb = new StringBuilder();
      foreach (var ouName in ouNames)
      {
        sb.AppendFormat("OU={0},", ouName);
      }
      foreach (var dc in DirectoryAccess.DomainName.Split('.'))
      {
        sb.AppendFormat("DC={0},", dc);
      }
      sb.Length--;
      return (OrganizationalUnit)DirectoryAccess.FindDirectoryObject(sb.ToString(), CategoryType.OrganizationalUnit);
    }
    #endregion

    #region プロテクトメソッド
    /// <summary>
    /// 使用されているリソースを解放し、オプションでマネージ リソースも解放します。
    /// </summary>
    /// <param name="disposing">マネージ リソースとアンマネージ リソースの両方を解放する場合は true。
    /// アンマネージ リソースだけを解放する場合は false。</param>
    protected override void Dispose(bool disposing)
    {
      if (_storedObjects != null)
      {
        DirectoryAccess.DisposeItems(_storedObjects);   //リソースを解放
        _storedObjects = null;
      }
      base.Dispose(disposing);
    }
    #endregion
  }
}
